; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --filter-out "(\.v?save|push|pop)"

; No FP16/BF16
; RUN: llc -mtriple=arm-none-eabi -float-abi=soft -mattr=+armv8.2-a,+fp-armv8,-fullfp16,-bf16,-neon %s -o - | FileCheck %s --check-prefixes=NO-FP16-SOFT
; RUN: llc -mtriple=thumb-none-eabi -float-abi=soft -mattr=+armv8.2-a,+fp-armv8,-fullfp16,-bf16,-neon %s -o - | FileCheck %s --check-prefixes=NO-FP16-SOFT
; RUN: llc -mtriple=arm-none-eabi -mattr=+armv8.2-a,+fp-armv8,-fullfp16,-bf16,-neon %s -o - | FileCheck %s --check-prefixes=NO-FP16-SOFT
; RUN: llc -mtriple=thumb-none-eabi -mattr=+armv8.2-a,+fp-armv8,-fullfp16,-bf16,-neon %s -o - | FileCheck %s --check-prefixes=NO-FP16-SOFT
; RUN: llc -mtriple=arm-none-eabihf -mattr=+armv8.2-a,+fp-armv8,-fullfp16,-bf16,-neon %s -o - | FileCheck %s --check-prefixes=NO-FP16-HARD
; RUN: llc -mtriple=thumb-none-eabihf -mattr=+armv8.2-a,+fp-armv8,-fullfp16,-bf16,-neon %s -o - | FileCheck %s --check-prefixes=NO-FP16-HARD

; FP16/BF16
; RUN: llc -mtriple=arm-none-eabi -float-abi=soft -mattr=+armv8.2-a,+fp-armv8,+fullfp16,+bf16,-neon %s -o - | FileCheck %s --check-prefixes=FP16-SOFT
; RUN: llc -mtriple=thumb-none-eabi -float-abi=soft -mattr=+armv8.2-a,+fp-armv8,+fullfp16,+bf16,-neon %s -o - | FileCheck %s --check-prefixes=FP16-SOFT
; RUN: llc -mtriple=arm-none-eabi -mattr=+armv8.2-a,+fp-armv8,+fullfp16,+bf16,-neon %s -o - | FileCheck %s --check-prefixes=FP16-SOFT
; RUN: llc -mtriple=thumb-none-eabi -mattr=+armv8.2-a,+fp-armv8,+fullfp16,+bf16,-neon %s -o - | FileCheck %s --check-prefixes=FP16-SOFT
; RUN: llc -mtriple=arm-none-eabihf -mattr=+armv8.2-a,+fp-armv8,+fullfp16,+bf16,-neon %s -o - | FileCheck %s --check-prefixes=FP16-HARD
; RUN: llc -mtriple=thumb-none-eabihf -mattr=+armv8.2-a,+fp-armv8,+fullfp16,+bf16,-neon %s -o - | FileCheck %s --check-prefixes=FP16-HARD

; PR60510 showed a bug where the return from `*_inner` was getting "lost" by an
; optimisation, and a garbage value was being left in `s0`.

declare dso_local float @other(float) nounwind
declare dso_local void @fp16_sink(half) nounwind
declare dso_local void @bf16_sink(bfloat) nounwind

declare dso_local bfloat @fp16_inner() nounwind
declare dso_local bfloat @bf_inner() nounwind

define half @fp16_out_call_oneuse(float %arg) nounwind {
; NO-FP16-SOFT-LABEL: fp16_out_call_oneuse:
; NO-FP16-SOFT:  @ %bb.0:
; NO-FP16-SOFT:    mov r4, r0
; NO-FP16-SOFT:    bl fp16_inner
; NO-FP16-SOFT:    mov r5, r0
; NO-FP16-SOFT:    mov r0, r4
; NO-FP16-SOFT:    bl other
; NO-FP16-SOFT:    mov r0, r5
;
; NO-FP16-HARD-LABEL: fp16_out_call_oneuse:
; NO-FP16-HARD:  @ %bb.0:
; NO-FP16-HARD:    vmov.f32 s16, s0
; NO-FP16-HARD:    bl fp16_inner
; NO-FP16-HARD:    vmov.f32 s18, s0
; NO-FP16-HARD:    vmov.f32 s0, s16
; NO-FP16-HARD:    bl other
; NO-FP16-HARD:    vmov.f32 s0, s18
;
; FP16-SOFT-LABEL: fp16_out_call_oneuse:
; FP16-SOFT:  @ %bb.0:
; FP16-SOFT:    mov r4, r0
; FP16-SOFT:    bl fp16_inner
; FP16-SOFT:    mov r5, r0
; FP16-SOFT:    mov r0, r4
; FP16-SOFT:    bl other
; FP16-SOFT:    vmov.f16 s0, r5
; FP16-SOFT:    vmov r0, s0
;
; FP16-HARD-LABEL: fp16_out_call_oneuse:
; FP16-HARD:  @ %bb.0:
; FP16-HARD:    vmov.f32 s16, s0
; FP16-HARD:    bl fp16_inner
; FP16-HARD:    vmov.f32 s18, s0
; FP16-HARD:    vmov.f32 s0, s16
; FP16-HARD:    bl other
; FP16-HARD:    vmov.f32 s0, s18
  %call = call half @fp16_inner()
  %call1 = call float @other(float %arg)
  ret half %call
}

define half @fp16_out_call_multiuse(float %arg) nounwind {
; NO-FP16-SOFT-LABEL: fp16_out_call_multiuse:
; NO-FP16-SOFT:  @ %bb.0:
; NO-FP16-SOFT:    mov r4, r0
; NO-FP16-SOFT:    bl fp16_inner
; NO-FP16-SOFT:    mov r5, r0
; NO-FP16-SOFT:    mov r0, r4
; NO-FP16-SOFT:    bl other
; NO-FP16-SOFT:    mov r0, r5
; NO-FP16-SOFT:    bl fp16_sink
; NO-FP16-SOFT:    mov r0, r5
;
; NO-FP16-HARD-LABEL: fp16_out_call_multiuse:
; NO-FP16-HARD:  @ %bb.0:
; NO-FP16-HARD:    vmov.f32 s16, s0
; NO-FP16-HARD:    bl fp16_inner
; NO-FP16-HARD:    vmov.f32 s18, s0
; NO-FP16-HARD:    vmov.f32 s0, s16
; NO-FP16-HARD:    bl other
; NO-FP16-HARD:    vmov.f32 s0, s18
; NO-FP16-HARD:    bl fp16_sink
; NO-FP16-HARD:    vmov.f32 s0, s18
;
; FP16-SOFT-LABEL: fp16_out_call_multiuse:
; FP16-SOFT:  @ %bb.0:
; FP16-SOFT:    mov r4, r0
; FP16-SOFT:    bl fp16_inner
; FP16-SOFT:    mov r5, r0
; FP16-SOFT:    mov r0, r4
; FP16-SOFT:    bl other
; FP16-SOFT:    vmov.f16 s16, r5
; FP16-SOFT:    vmov.f16 r0, s16
; FP16-SOFT:    bl fp16_sink
; FP16-SOFT:    vmov r0, s16
;
; FP16-HARD-LABEL: fp16_out_call_multiuse:
; FP16-HARD:  @ %bb.0:
; FP16-HARD:    vmov.f32 s16, s0
; FP16-HARD:    bl fp16_inner
; FP16-HARD:    vmov.f32 s18, s0
; FP16-HARD:    vmov.f32 s0, s16
; FP16-HARD:    bl other
; FP16-HARD:    vmov.f16 r0, s18
; FP16-HARD:    vmov s0, r0
; FP16-HARD:    bl fp16_sink
; FP16-HARD:    vmov.f32 s0, s18
  %call = call half @fp16_inner()
  %call1 = call float @other(float %arg)
  call void @fp16_sink(half %call)
  ret half %call
}

define bfloat @bf_out_call_oneuse(float %arg) nounwind {
; NO-FP16-SOFT-LABEL: bf_out_call_oneuse:
; NO-FP16-SOFT:  @ %bb.0:
; NO-FP16-SOFT:    mov r4, r0
; NO-FP16-SOFT:    bl bf_inner
; NO-FP16-SOFT:    mov r5, r0
; NO-FP16-SOFT:    mov r0, r4
; NO-FP16-SOFT:    bl other
; NO-FP16-SOFT:    mov r0, r5
;
; NO-FP16-HARD-LABEL: bf_out_call_oneuse:
; NO-FP16-HARD:  @ %bb.0:
; NO-FP16-HARD:    vmov.f32 s16, s0
; NO-FP16-HARD:    bl bf_inner
; NO-FP16-HARD:    vmov.f32 s18, s0
; NO-FP16-HARD:    vmov.f32 s0, s16
; NO-FP16-HARD:    bl other
; NO-FP16-HARD:    vmov.f32 s0, s18
;
; FP16-SOFT-LABEL: bf_out_call_oneuse:
; FP16-SOFT:  @ %bb.0:
; FP16-SOFT:    mov r4, r0
; FP16-SOFT:    bl bf_inner
; FP16-SOFT:    mov r5, r0
; FP16-SOFT:    mov r0, r4
; FP16-SOFT:    bl other
; FP16-SOFT:    mov r0, r5
;
; FP16-HARD-LABEL: bf_out_call_oneuse:
; FP16-HARD:  @ %bb.0:
; FP16-HARD:    vmov.f32 s16, s0
; FP16-HARD:    bl bf_inner
; FP16-HARD:    vmov.f32 s18, s0
; FP16-HARD:    vmov.f32 s0, s16
; FP16-HARD:    bl other
; FP16-HARD:    vmov.f32 s0, s18
  %call = call bfloat @bf_inner()
  %call1 = call float @other(float %arg)
  ret bfloat %call
}

define bfloat @bf_out_call_multiuse(float %arg) nounwind {
; NO-FP16-SOFT-LABEL: bf_out_call_multiuse:
; NO-FP16-SOFT:  @ %bb.0:
; NO-FP16-SOFT:    mov r4, r0
; NO-FP16-SOFT:    bl bf_inner
; NO-FP16-SOFT:    mov r5, r0
; NO-FP16-SOFT:    mov r0, r4
; NO-FP16-SOFT:    bl other
; NO-FP16-SOFT:    mov r0, r5
; NO-FP16-SOFT:    bl bf16_sink
; NO-FP16-SOFT:    mov r0, r5
;
; NO-FP16-HARD-LABEL: bf_out_call_multiuse:
; NO-FP16-HARD:  @ %bb.0:
; NO-FP16-HARD:    vmov.f32 s16, s0
; NO-FP16-HARD:    bl bf_inner
; NO-FP16-HARD:    vmov.f32 s18, s0
; NO-FP16-HARD:    vmov.f32 s0, s16
; NO-FP16-HARD:    bl other
; NO-FP16-HARD:    vmov.f32 s0, s18
; NO-FP16-HARD:    bl bf16_sink
; NO-FP16-HARD:    vmov.f32 s0, s18
;
; FP16-SOFT-LABEL: bf_out_call_multiuse:
; FP16-SOFT:  @ %bb.0:
; FP16-SOFT:    mov r4, r0
; FP16-SOFT:    bl bf_inner
; FP16-SOFT:    mov r5, r0
; FP16-SOFT:    mov r0, r4
; FP16-SOFT:    bl other
; FP16-SOFT:    mov r0, r5
; FP16-SOFT:    bl bf16_sink
; FP16-SOFT:    mov r0, r5
;
; FP16-HARD-LABEL: bf_out_call_multiuse:
; FP16-HARD:  @ %bb.0:
; FP16-HARD:    vmov.f32 s16, s0
; FP16-HARD:    bl bf_inner
; FP16-HARD:    vmov.f32 s18, s0
; FP16-HARD:    vmov.f32 s0, s16
; FP16-HARD:    bl other
; FP16-HARD:    vmov.f32 s0, s18
; FP16-HARD:    bl bf16_sink
; FP16-HARD:    vmov.f32 s0, s18
  %call = call bfloat @bf_inner()
  %call1 = call float @other(float %arg)
  call void @bf16_sink(bfloat %call)
  ret bfloat %call
}
